#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include "mocks.h"
#include "cppsaxxmlparser.hpp"

using namespace std;
using namespace CppSaxXmlParser;
using ::testing::_;
using ::testing::InSequence;

//HandlesValidXmlHeader

shared_ptr<MockIXmlListener> prepareMockForValidHeaderTest()
{
	shared_ptr<MockIXmlListener> result(new MockIXmlListener);
	EXPECT_CALL(*result, onHeaderOpen(_)).Times(1);
	EXPECT_CALL(*result, onHeaderClose(_)).Times(1);
	EXPECT_CALL(*result, onTagOpen(_, _)).Times(0);
	EXPECT_CALL(*result, onTagClose(_, _)).Times(0);
	EXPECT_CALL(*result, onAttribute(_, wstring(L"version"), wstring(L"1.0"))).Times(1);

	return result;
}

TEST(ParserTests, HandlesValidXmlHeader)
{
	{
		shared_ptr<MockIXmlListener> mockListener = prepareMockForValidHeaderTest();
		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<?xml version=\"1.0\"?>";

		Parser parser(*mockListener, ss);
		parser.parse();
	}

	{
		shared_ptr<MockIXmlListener> mockListener = prepareMockForValidHeaderTest();
		wstringstream ss(stringstream::in | stringstream::out);
		ss << L" <?xml version=\"1.0\" ?>";

		Parser parser(*mockListener, ss);
		parser.parse();
	}

	{
		shared_ptr<MockIXmlListener> mockListener = prepareMockForValidHeaderTest();
		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<?xml version\n=\t\"1.0\"    ?>";

		Parser parser(*mockListener, ss);
		parser.parse();
	}
}

//HandlesInvalidXmlHeader

TEST(ParserTests, HandlesInvalidXmlHeader)
{
	{
		MockIXmlListener mockListener;
		wstringstream ss(stringstream::in | stringstream::out);

		Parser parser(mockListener, ss);
		ASSERT_THROW(parser.parse(), std::exception);
	}

	{
		MockIXmlListener mockListener;
		EXPECT_CALL(mockListener, onHeaderOpen(_)).Times(1);
		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<?xml version=\1.0\"?>";

		Parser parser(mockListener, ss);
		ASSERT_THROW(parser.parse(), std::exception);
	}

	{
		MockIXmlListener mockListener;
		EXPECT_CALL(mockListener, onHeaderOpen(_)).Times(1);
		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<?xml version \"1.0\"?>";

		Parser parser(mockListener, ss);
		ASSERT_THROW(parser.parse(), std::exception);
	}

	{
		MockIXmlListener mockListener;
		EXPECT_CALL(mockListener, onHeaderOpen(_)).Times(1);
		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<?xml version?>";

		Parser parser(mockListener, ss);
		ASSERT_THROW(parser.parse(), std::exception);
	}

	{
		MockIXmlListener mockListener;
		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"sdf <?xml version=\"1.0\"?>";

		Parser parser(mockListener, ss);
		ASSERT_THROW(parser.parse(), std::exception);
	}
}

TEST(ParserTests, ValidXml)
{
	{
		MockIXmlListener mockListener;
		{
			InSequence s;
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"root"))).Times(1);
		}

		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<root/>";

		Parser parser(mockListener, ss);
		parser.parse();
	}

	{
		MockIXmlListener mockListener;
		{
			InSequence s;
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"root"))).Times(1);
		}

		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<root></root>";

		Parser parser(mockListener, ss);
		parser.parse();
	}

	{
		MockIXmlListener mockListener;
		{
			InSequence s;
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L"123") ,wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"root"))).Times(1);
		}

		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<root>123</root>";

		Parser parser(mockListener, ss);
		parser.parse();
	}

	{
		MockIXmlListener mockListener;
		{
			InSequence s;
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L"123") ,wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"root"))).Times(1);
		}

		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<root>123<nested/></root>";

		Parser parser(mockListener, ss);
		parser.parse();
	}

	{
		MockIXmlListener mockListener;
		{
			InSequence s;
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L"123") ,wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"root"))).Times(1);
		}

		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<root>123<nested /></root>";

		Parser parser(mockListener, ss);
		parser.parse();
	}

	{
		MockIXmlListener mockListener;
		{
			InSequence s;
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L"123") ,wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L" ") ,wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"root"))).Times(1);
		}

		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<root>123<nested> </nested></root>";

		Parser parser(mockListener, ss);
		parser.parse();
	}

	{
		MockIXmlListener mockListener;
		{
			InSequence s;
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L"123") ,wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L" ") ,wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L"123 /> ") ,wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"root"))).Times(1);
		}

		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<root>123<nested> </nested>123 /> </root>";

		Parser parser(mockListener, ss);
		parser.parse();
	}

	{
		MockIXmlListener mockListener;
		{
			InSequence s;
			EXPECT_CALL(mockListener, onHeaderOpen(_)).Times(1);
			EXPECT_CALL(mockListener, onAttribute(_, wstring(L"version"), wstring(L"1.0"))).Times(1);
			EXPECT_CALL(mockListener, onHeaderClose(_)).Times(1);
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L"123") ,wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L" ") ,wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"nested"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L"123 /> ") ,wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"root"))).Times(1);
		}

		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<?xml version=\"1.0\"?><root>123<nested> </nested>123 /> </root>";

		Parser parser(mockListener, ss);
		parser.parse();
	}

	{
		MockIXmlListener mockListener;
		{
			InSequence s;
			EXPECT_CALL(mockListener, onHeaderOpen(_)).Times(1);
			EXPECT_CALL(mockListener, onAttribute(_, wstring(L"version"), wstring(L"1.0"))).Times(1);
			EXPECT_CALL(mockListener, onHeaderClose(_)).Times(1);
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"stream:stream"))).Times(1);
			EXPECT_CALL(mockListener, onAttribute(_, wstring(L"xmlns:stream"), wstring(L"http://etherx.jabber.org/streams"))).Times(1);
			EXPECT_CALL(mockListener, onAttribute(_, wstring(L"version"), wstring(L"1.0"))).Times(1);
			EXPECT_CALL(mockListener, onAttribute(_, wstring(L"xmlns"), wstring(L"jabber:client"))).Times(1);
			EXPECT_CALL(mockListener, onAttribute(_, wstring(L"to"), wstring(L"127.0.0.1"))).Times(1);
			EXPECT_CALL(mockListener, onAttribute(_, wstring(L"xml:lang"), wstring(L"en"))).Times(1);
			EXPECT_CALL(mockListener, onAttribute(_, wstring(L"xmlns:xml"), wstring(L"http://www.w3.org/XML/1998/namespace"))).Times(1);
		}

		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<?xml version=\"1.0\"?> \
			<stream:stream xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\" \
			xmlns=\"jabber:client\" to=\"127.0.0.1\" xml:lang=\"en\" xmlns:xml=\"http://www.w3.org/XML/1998/namespace\" > \
			";

		Parser parser(mockListener, ss);
		parser.parse();
	}
}

TEST(ParserTests, ParseComments)
{
	{
		MockIXmlListener mockListener;
		{
			InSequence s;
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L"123") ,wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"root"))).Times(1);
		}

		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<root>123<!--nested> </nested>123 /--></root>";

		Parser parser(mockListener, ss);
		parser.parse();
	}
}

TEST(ParserTests, ParseCDATA)
{
	{
		MockIXmlListener mockListener;
		{
			InSequence s;
			EXPECT_CALL(mockListener, onTagOpen(_, wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onTagText(_, wstring(L"123") ,wstring(L"root"))).Times(1);
			EXPECT_CALL(mockListener, onCDATA(_, wstring(L"nested> </nested>123 /"))).Times(1);
			EXPECT_CALL(mockListener, onTagClose(_, wstring(L"root"))).Times(1);
		}

		wstringstream ss(stringstream::in | stringstream::out);
		ss << L"<root>123<![CDATA[nested> </nested>123 /]]></root>";

		Parser parser(mockListener, ss);
		parser.parse();
	}
}